#include "../include/molenet.h"

#include "../include/common.h"
#include "../include/glog/logging.h"
#include "../include/libuv/uv.h"
#include "../include/lua/lua.h"
#include "../include/netallocator/NedAllocatorImpl.h"
#include "../include/socketmanager.h"
#include "../include/breakpad/exception_handler.h"
#include "html5/sha1.h"
#include "html5/base64.h"
#include "html5/WebsocketDataMessage.h"
#include "html5/WebsocketHandshakeMessage.h"
#include "../include/lua_extend.h"
#include "../include/debuger/ldb.h"
#include "json.h"

#include <direct.h>
#include <string.h>

const char *scriptcontent = "function _G.reload(scriptPath) package.loaded[scriptPath] = nil;return require(scriptPath);end function loadscript(scriptpath) reload(scriptpath); end";

/*const char *scriptcontent = "function _G.reloadstring(scriptPath,scriptString)"
							"	package.loaded[scriptPath] = nil;"
							"	local func, err = load(scriptString);"
							"   package.loaded[scriptPath] = pcall(func);"
							"end "
							"function loadscriptstring(scriptpath,scriptstring)"
							"	reloadstring(scriptpath,scriptstring);"
							"end";*/

uint32 m_scriptchangedcount = 0;
uint64 m_scriptchangetime = 0;

bool dump_callback(const wchar_t *dump_path, const wchar_t *id, void *content, EXCEPTION_POINTERS *exinfo, MDRawAssertionInfo *assertion, bool succeeded)
{
	if (succeeded)
	{
		LOG(ERROR) << "系统已经崩溃,dmp已经生成成功,请联系开发人员!";
		SendLog("系统已经崩溃,dmp已经生成成功,请联系开发人员!", __FILE__, __LINE__, "error");
	}
	else
	{
		LOG(ERROR) << "系统已经崩溃,dmp生成失败,请联系开发人员!";
		SendLog("系统已经崩溃,dmp生成失败,请联系开发人员!", __FILE__, __LINE__, "error");
	}

	return succeeded;
}

// 核查目录，若目录不存在，创建目录
bool FindOrCreateDirectory(const char* pszPath)
{
	WIN32_FIND_DATAA fd;
	HANDLE hFind = ::FindFirstFileA(pszPath, &fd);
	while (hFind != INVALID_HANDLE_VALUE)
	{
		if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
			return true;
	}

	if (!::CreateDirectoryA(pszPath, NULL))
	{
		char szDir[MAX_PATH];
		sprintf_s(szDir, sizeof(szDir), "创建目录[%s]失败,请检查权限", pszPath);
		LOG(ERROR) << szDir;
		SendLog(szDir, __FILE__, __LINE__,"error");

		return false;
	}

	return true;
}

void wait_for_a_while(uv_idle_t* handle) 
{
	messagemanager mymessagemanager;
	GetNetMessage(mymessagemanager);

	for (int i = 0; i < mymessagemanager.GetCount(); i++)
	{
		MessageStru *mes = mymessagemanager.GetMesById(i);
		if (mes == NULL) continue;

		switch (mes->GetType())
		{
		case MES_TYPE_ON_CONNECTED:
		{
			LOG(INFO) << "MES_TYPE_ON_CONNECTED";
			SendLog("MES_TYPE_ON_CONNECTED", __FILE__, __LINE__,"info");
		}
		break;
		case MES_TYPE_ON_DISCONNECTED:
		{
			LOG(INFO) << "MES_TYPE_ON_DISCONNECTED";
			SendLog("MES_TYPE_ON_DISCONNECTED", __FILE__, __LINE__,"info");
		}
		break;
		case MES_TYPE_ON_READ:
		{
			nlohmann::json reciverData = nlohmann::json::parse(mes->GetMes()->getData(), nullptr, false);
			
			if (!reciverData.is_discarded())
			{
				//printf("MES_TYPE_ON_READ:%d\n", reciverData["MsgId"].get<int>());

				switch (reciverData["MsgId"].get<int>())
				{
				case IDD_MESSAGE_UPDATESCRIPT:
				{
					std::string filename = reciverData["file"].get < std::string >();
					std::string content = reciverData["content"].get < std::string >();

					if (filename.empty() || content.empty())
						continue;

					char exeFullPath[MAX_PATH];
					GetModuleFileNameA(NULL, exeFullPath, MAX_PATH);
					std::string pexeFullPath = exeFullPath;
					int pos = pexeFullPath.find_last_of("\\");
					if (pos > 0)
						pexeFullPath = pexeFullPath.substr(0, pos + 1);
					pexeFullPath += "scripts\\";
					pexeFullPath += filename;

					std::string p_script_content = base64_decode(content);
					bool issuccesed = false;
					
					FILE *fp = fopen(pexeFullPath.c_str(), "w");
					if (fp != NULL)
					{
						if (fwrite(p_script_content.c_str(), p_script_content.length(), 1, fp) > 0)
							issuccesed = true;

						fclose(fp);
					}

					nlohmann::json jsondata;
					jsondata["MsgId"] = IDD_MESSAGE_UPDATESCRIPT;
					jsondata["file"] = filename;
					jsondata["state"] = issuccesed;
					auto str = jsondata.dump();

					SendH5All((char*)str.c_str(), str.length());

					continue;
				}
				break;
				case IDD_MESSAGE_GETSCRIPT_FILES:
				{
					char exeFullPath[MAX_PATH];
					GetModuleFileNameA(NULL, exeFullPath, MAX_PATH);
					std::string pexeFullPath = exeFullPath;
					int pos = pexeFullPath.find_last_of("\\");
					if (pos > 0)
						pexeFullPath = pexeFullPath.substr(0, pos + 1);
					pexeFullPath += "scripts";

					std::vector<std::string> pallfiles;
					getAllFiles(pexeFullPath, pallfiles);

					nlohmann::json jsondata;
					jsondata["MsgId"] = IDD_MESSAGE_GETSCRIPT_FILES;

					auto jarray = nlohmann::json::array();

					for (int i = 0; i < pallfiles.size(); i++)
					{
						std::string ptempfile = pallfiles[i];
						int pos = ptempfile.find(pexeFullPath);
						if (pos >= 0)
							ptempfile = ptempfile.substr(pos + pexeFullPath.length()+1, ptempfile.length());

						nlohmann::json j3;
						j3["file"] = ptempfile;

						jarray.emplace_back(j3);
					}

					jsondata["data"] = jarray;

					auto str = jsondata.dump();

					SendH5All((char*)str.c_str(), str.length());
					continue;
				}
					break;
				case IDD_MESSAGE_GETSCRIPT_CONTENT:
				{
					std::string filename = reciverData["file"].get < std::string > ();

					char exeFullPath[MAX_PATH];
					GetModuleFileNameA(NULL, exeFullPath, MAX_PATH);
					std::string pexeFullPath = exeFullPath;
					int pos = pexeFullPath.find_last_of("\\");
					if (pos > 0)
						pexeFullPath = pexeFullPath.substr(0, pos + 1);
					pexeFullPath += "scripts\\";
					pexeFullPath += filename;

					std::string p_script_content;

					FILE *fp = fopen(pexeFullPath.c_str(), "r");
					if (fp != NULL)
					{
						fseek(fp, 0, SEEK_END);
						int length = ftell(fp);
						fseek(fp, 0, SEEK_SET);

						if (length > 0)
						{
							char *buffer = (char*)allocBytes(length);
							memset(buffer, 0, sizeof(buffer));

							fread(buffer, length, 1, fp);
							p_script_content = buffer;

							deallocBytes(buffer);
						}

						fclose(fp);
					}

					nlohmann::json jsondata;
					jsondata["MsgId"] = IDD_MESSAGE_GETSCRIPT_CONTENT;
					jsondata["content"] = p_script_content.empty() ? "" : base64_encode((unsigned char const*)p_script_content.c_str(),p_script_content.length());
					auto str = jsondata.dump();

					SendH5All((char*)str.c_str(), str.length());
					continue;
				}
				break;
				default:
					break;
				}
			}

			lua_network_callback(mes->GetSocket()->getSocketID(), mes->GetMes()->getData());
		}
		break;
		default:
			break;
		}
	}

	// 是否重新加载脚本文件
	if (m_scriptchangedcount > 0
		&& ((uint64)time(NULL) - m_scriptchangetime >= 2))
	{
		m_scriptchangedcount = 0;
		m_scriptchangetime = 0;

		clearalltimers();
		//ldb_closesocket();
		lua_loadscript_callback("scripts\\main");
		lua_setup_callback();
	}

	Sleep(10);
}

void on_client_closed(uv_handle_t* handle)
{
	uv_tcp_t *client = (uv_tcp_t*)(handle);

	if (client->data)
		deallocBytes(client->data);

	deallocBytes(client);
}

void on_clients_update(uv_timer_t *handle)
{
	std::vector<Socket*> pclients = sSocketMgr.getDisconnectedClients();
	for (int i = 0; i < (int)pclients.size(); i++)
	{
		if (pclients[i] == NULL || 
			pclients[i]->getClient() == NULL ||
			pclients[i]->getSocketID() > 65555) 
			continue;

		uv_tcp_t *pclient = pclients[i]->getClient();
		if (pclient)
		{
			sSocketMgr.AddMessage(MessageStru(MES_TYPE_ON_DISCONNECTED, sSocketMgr.FindSocket(pclient->socket)));
			sSocketMgr.DeleteSocket(sSocketMgr.FindSocket(pclient->socket));
		}

		uv_close((uv_handle_t*)pclient, on_client_closed);
	}
}

void on_alloc_buffer(uv_handle_t* handle, size_t suggested_size, uv_buf_t* buf)
{
	buf->base = static_cast<char*>(MemoryMalloc(suggested_size));
	buf->len = suggested_size;
}

void on_peer_read(uv_stream_t* client, ssize_t nread, const uv_buf_t* buf)
{
	if (nread < 0)
	{
		if (nread != UV_EOF)
		{
			LOG(ERROR) << "read error:" << uv_strerror(nread);
			SendLog(uv_strerror(nread), __FILE__, __LINE__,"error");
		}

		uv_tcp_t *pclient = (uv_tcp_t*)client;
		if (pclient)
		{
			sSocketMgr.AddMessage(MessageStru(MES_TYPE_ON_DISCONNECTED, sSocketMgr.FindSocket(pclient->socket)));
			sSocketMgr.DeleteSocket(sSocketMgr.FindSocket(pclient->socket));
		}	

		uv_close((uv_handle_t*)client, on_client_closed);
	}
	else if (nread == 0)
	{

	}
	else
	{
		if (buf->base && nread > 0)
		{
			Socket *psocket = sSocketMgr.FindSocket(((uv_tcp_t*)client)->socket);
			if (psocket)
			{
				psocket->OnRead(buf->base, nread);
			}

			deallocBytes(buf->base);
		}
	}
}

void on_peer_connected(uv_stream_t *server, int status)
{
	if (status < 0)
	{
		LOG(ERROR) << "peer connection error:%s\n", uv_strerror(status);
		SendLog(uv_strerror(status), __FILE__, __LINE__,"error");
		return;
	}

	uv_tcp_t* client = (uv_tcp_t*)allocBytes(sizeof(*client));

	int rc = 0;
	if ((rc = uv_tcp_init(uv_default_loop(), client)) < 0)
	{
		LOG(ERROR) << "uv_tcp_init failed:%s\n", uv_strerror(rc);
		SendLog(uv_strerror(rc), __FILE__, __LINE__,"error");
	}
	client->data = NULL;

	if (uv_accept(server, (uv_stream_t*)client) == 0)
	{
		struct sockaddr_storage peername;
		int namelen = sizeof(peername);

		if ((rc = uv_tcp_getpeername(client, (struct sockaddr*)&peername, &namelen)) < 0)
		{
			LOG(ERROR) << "uv_tcp_getpeername failed:%s\n", uv_strerror(rc);
			SendLog(uv_strerror(rc), __FILE__, __LINE__,"error");
		}

		Socket *pSocket = sSocketMgr.FindSocket(client->socket);
		if (pSocket == NULL)
		{
			sSocketMgr.AddSocket(new Socket(client, 245760, 32768));
			sSocketMgr.AddMessage(MessageStru(MES_TYPE_ON_CONNECTED, sSocketMgr.FindSocket(client->socket)));
		}
		else
		{
			pSocket->reset(client);
		}

		if ((rc = uv_read_start((uv_stream_t*)client, on_alloc_buffer, on_peer_read)) < 0)
		{
			LOG(ERROR) << "uv_read_start failed:%s\n", uv_strerror(rc);
			SendLog(uv_strerror(rc), __FILE__, __LINE__,"error");
		}
	}
	else
	{
		uv_tcp_t *pclient = (uv_tcp_t*)client;
		if (pclient)
		{
			sSocketMgr.AddMessage(MessageStru(MES_TYPE_ON_DISCONNECTED, sSocketMgr.FindSocket(pclient->socket)));
			sSocketMgr.DeleteSocket(sSocketMgr.FindSocket(pclient->socket));
		}

		uv_close((uv_handle_t*)client, on_client_closed);
	}
}

void on_file_changed(uv_fs_event_t *handle, const char *filename, int events, int status)
{
	if (events & UV_CHANGE)
	{
		if (m_scriptchangetime == 0)
		{
			m_scriptchangedcount = 0;
		}

		m_scriptchangedcount += 1;
		m_scriptchangetime = (uint64)time(NULL);
	}
}

void SendLog(const char *msg, const char *file, int line, const char *type)
{
	if (msg == NULL || file == NULL || line <= 0)
		return;

	std::string tmpstr = getlogBytime(msg, file, line,type);

	nlohmann::json jsondata;
	jsondata["MsgId"] = IDD_MESSAGE_LOG;
	jsondata["type"] = type;
	jsondata["content"] = tmpstr;

	//auto jarray = nlohmann::json::array();
	//jarray.emplace_back(jsondata);
	auto jsonsenddata = jsondata.dump();

	SendH5All((char*)jsonsenddata.c_str(), jsonsenddata.length());
}

/**
 * 开始网络服务
 *
 * @param ListenAddress 侦听的网络地址
 * @param Port 侦听的服务器端口
 *
 * @return 如果网络服务启动成功返回真,否则返回假
 */
bool StartMolNet(const char * ListenAddress, uint32 Port, uint32 dPort)
{
	if (ListenAddress == NULL || Port <= 0)
		return false;

	ldb_startsocket(get_luastate(), ListenAddress, dPort);

	uv_loop_t *loop = uv_default_loop();

	int rc;
	uv_tcp_t server_stream;
	if ((rc = uv_tcp_init(loop, &server_stream)) < 0)
	{
		LOG(ERROR) << "uv_tcp_init failed:%s\n", uv_strerror(rc);
		SendLog(uv_strerror(rc), __FILE__, __LINE__,"error");
		return false;
	}

	char exeFullPath[MAX_PATH];
	GetModuleFileNameA(NULL, exeFullPath,MAX_PATH);
	std::string pexeFullPath = exeFullPath;
	int pos = pexeFullPath.find_last_of("\\");
	if (pos > 0)
		pexeFullPath = pexeFullPath.substr(0, pos+1);
	pexeFullPath += "scripts";

	LOG(INFO) << "uv_tcp_init successed:" << pexeFullPath.c_str();
	SendLog(pexeFullPath.c_str(), __FILE__, __LINE__,"info");

	struct sockaddr_in server_address;
	if ((rc = uv_ip4_addr(ListenAddress, Port, &server_address)))
	{
		LOG(ERROR) << "uv_ip4_addr failed:%s\n", uv_strerror(rc);
		SendLog(uv_strerror(rc), __FILE__, __LINE__,"error");
		return false;
	}

	if ((rc = uv_tcp_bind(&server_stream, (const struct sockaddr*)&server_address, 0)) < 0)
	{
		LOG(ERROR) << "uv_tcp_bind failed:%s\n", uv_strerror(rc);
		SendLog(uv_strerror(rc), __FILE__, __LINE__,"error");
		return false;
	}

	LOG(INFO) << "server tcp bind successed,server start listen,port:" << Port;

	if ((rc = uv_listen((uv_stream_t*)&server_stream, N_BACKLOG, on_peer_connected)) < 0)
	{
		LOG(ERROR) << "uv_listen failed:%s", uv_strerror(rc);
		SendLog(uv_strerror(rc), __FILE__, __LINE__,"error");
		return false;
	}

	uv_timer_t clientsupdate_timer_req;
	uv_timer_init(loop, &clientsupdate_timer_req);
	uv_timer_start(&clientsupdate_timer_req, on_clients_update, 1000, 1000);

	uv_idle_t idler;
	uv_idle_init(loop, &idler);
	uv_idle_start(&idler, wait_for_a_while);

	uv_fs_event_t fs_event_req;
	uv_fs_event_init(loop, &fs_event_req);
	uv_fs_event_start(&fs_event_req, on_file_changed, pexeFullPath.c_str(), UV_FS_EVENT_WATCH_ENTRY);

	clearalltimers();
	//ldb_closesocket();
	lua_loadscript_callback("scripts\\main");
	lua_setup_callback();

	uv_run(loop, UV_RUN_DEFAULT);

	uv_loop_close(loop);

	return true;
}

/**
 * 初始网络，设置相应的参数
 */
void InitMolNet(int argc, char* argv[])
{
	new SocketMgr();

	char exeFullPath[MAX_PATH];
	GetModuleFileNameA(NULL, exeFullPath, MAX_PATH);
	std::string pexeFullPath = exeFullPath;
	int pos = pexeFullPath.find_last_of("\\");
	if (pos > 0)
		pexeFullPath = pexeFullPath.substr(0, pos+1);
	pexeFullPath += "log";

	FindOrCreateDirectory(pexeFullPath.c_str());

	pexeFullPath += "\\";

	google::InitGoogleLogging(argv[0]);
	google::SetLogDestination(google::GLOG_INFO, pexeFullPath.c_str());

	google_breakpad::ExceptionHandler eh(
		L".", NULL, dump_callback, NULL, google_breakpad::ExceptionHandler::HANDLER_ALL);

	lua_init();
	reg_all2luas();
	exe_lua_string(scriptcontent,strlen(scriptcontent));
}

/**
 * 卸载网络
 */
void CleanMolNet(void)
{
	clearalltimers();
	delete SocketMgr::getSingletonPtr();
	google::ShutdownGoogleLogging();

	lua_exit();
}

/// 得到消息列表
int GetNetMessage(messagemanager & mes)
{
	mes.Clear();

	if (sSocketMgr.GetCount() <= 0 ||
		mes.GetMaxCount() <= 0)
		return 0;

	int count = 0;

	// 如果当前系统中的消息个数小于我们要读取的个数时，读取全部的消息；
	// 否则读取我们设置的消息个数的消息
	if (sSocketMgr.GetCount() < mes.GetMaxCount())
	{
		std::vector<MessageStru>* meslist = sSocketMgr.GetMessageList();
		if (meslist == NULL || meslist->empty())
		{
			return 0;
		}

		std::vector<MessageStru>::iterator iter = meslist->begin();
		for (; iter != meslist->end();)
		{
			mes.AddMessage((*iter));
			iter = meslist->erase(iter);
			count += 1;
		}
	}
	else
	{
		std::vector<MessageStru> *meslist = sSocketMgr.GetMessageList();
		if (meslist == NULL || meslist->empty())
		{
			return 0;
		}

		std::vector<MessageStru>::iterator iter = meslist->begin();
		for (int i = 0; iter != meslist->end(), i < mes.GetMaxCount(); i++)
		{
			if (iter == meslist->end()) break;

			mes.AddMessage((*iter));
			iter = meslist->erase(iter);
			count += 1;
		}
	}

	return count;
}

bool Send(uint32 index, char *out, uint32 Size)
{
	if (index <= 0 || out == NULL || Size <= 0)
		return false;

	return sSocketMgr.Send(sSocketMgr.FindSocket(index), out, Size);
}

bool SendOther(uint32 index, char * Bytes, uint32 Size)
{
	if (index <= 0 || Bytes == NULL || Size <= 0)
		return false;

	return sSocketMgr.SendOther(sSocketMgr.FindSocket(index), Bytes, Size);
}

bool SendAll(char * Bytes, uint32 Size)
{
	if (Bytes == NULL || Size <= 0)
		return false;

	return sSocketMgr.SendAll(Bytes, Size);
}

bool SendH5(uint32 index, char *out, uint32 Size)
{
	if (index <= 0 || out == NULL || Size <= 0)
		return false;

	return sSocketMgr.SendH5(sSocketMgr.FindSocket(index), out, Size);
}

bool SendH5Other(uint32 index, char * Bytes, uint32 Size)
{
	if (index <= 0 || Bytes == NULL || Size <= 0)
		return false;

	return sSocketMgr.SendH5Other(sSocketMgr.FindSocket(index), Bytes, Size);
}

bool SendH5All(char * Bytes, uint32 Size)
{
	if (Bytes == NULL || Size <= 0)
		return false;

	return sSocketMgr.SendH5All(Bytes, Size);
}